#include "ImageMean.h"

#include "kNNClassifier.h"
#include "DataSet.h"
#include "Sample.h"
#include "Sample.h"
#include "Image.H"
#include "IO.H"
#include "Util.H"
#include<limits> 	
#include <iostream>

const unsigned int ImageMean::window[3][9] = { { 288,289,290,317,316,318,345,344,346},
									  { 430,431,432,458,459,460,486,487,488},
									  { 206,207,231,232,233,234,235,263,262} };


ImageMean::ImageMean()
{
	knn = new kNNClassifier(7);
}

ImageMean::~ImageMean()
{
   delete knn;
}

int ImageMean::test(const Sample& s) const
{  //init
  unsigned int nearest(-1);
  float distance(-1),best(std::numeric_limits<float>::max());
  // for all prototypes measure distance (in all dimensions)
  for(unsigned int candidate=0; candidate < means_.size();++candidate){
	  distance=0;
	  for(unsigned int dimension=0; dimension < means_[1].size() ;++dimension){
		  distance+=   (s.input(dimension) - means_[candidate][dimension])
			         * (s.input(dimension) - means_[candidate][dimension]);
	  } 
	  //distance=sqrt(distance); // assume euclidian metric
	  if(distance < best){
		  best=distance;
		  nearest=candidate;//remember the best fit
	  }
  }
  return nearest; // return it
}

int ImageMean::testknn(const Sample& s) const
{  //init
  std::vector<float> dists;
  float distance(-1),best(std::numeric_limits<float>::max());
  // for all prototypes measure distance (in all dimensions)
  for(unsigned int candidate=0; candidate < means_.size();++candidate){
	  distance=0;
	  for(unsigned int dimension=0; dimension < means_[1].size() ;++dimension){
		  distance+=   (s.input(dimension) - means_[candidate][dimension])
					 * (s.input(dimension) - means_[candidate][dimension]);
	  } 
	  dists.push_back(distance);
	  for(int win=0; win < 3;++win){
			  unsigned char distance=0;
			for(unsigned int dimension=0; dimension < 9 ;++dimension){
				distance+=s.input(window[win][dimension]);
			}
			unsigned int here = distance/9;
			dists.push_back(here);
		  }
	  
  }
  Sample s2(dists);
  return knn->test(s2);
}

void ImageMean::trainknn(const DataSet& ds) const
{  //init
	DataSet ds2;
	int size(ds.samples().size());
	Sample s;
	std::vector<float> dists;
	for(int i=0; i < size ;++i){
      s=ds.sample(i);
	  dists.clear();
	  float distance(-1),best(std::numeric_limits<float>::max());
	  // for all prototypes measure distance (in all dimensions)
	  for(unsigned int candidate=0; candidate < means_.size();++candidate){
		  distance=0;
		  for(unsigned int dimension=0; dimension < means_[1].size() ;++dimension){
			  distance+=   (s.input(dimension) - means_[candidate][dimension])
						 * (s.input(dimension) - means_[candidate][dimension]);
		  } 
		  dists.push_back(distance);
		  
		  for(int win=0; win < 3;++win){
			  unsigned char distance=0;
			for(unsigned int dimension=0; dimension < 9 ;++dimension){
				distance+=s.input(window[win][dimension]);
			}
			unsigned int here = distance/9;
			dists.push_back(here);
		  }
	  }
	  ds2.push_back(Sample(dists,s.label()));
	
	for(int i=0; i < size ;i=i+100){
	for (unsigned int candidate=0; candidate < means_.size();++candidate){
		//std::cout << "distance " << dists[candidate] << std::endl;
	}}
	}
	knn->train(ds2);
	
}


void ImageMean::train(const DataSet& ds)
{
	// init
	std::cout << "\n nr_samples "<< ds.samples().size() << "\n";
	std::cout << "\n nr_features "<< ds.sample(0).input().size() << "\n";
  int current_label(0),
	  size(ds.samples().size()),
	  features(ds.sample(0).input().size()),
	  max(-1);
  // find max label - assume number of labels
  for(int s=0; s < size; ++s){
	if(ds.label(s) > max)
		max = ds.sample(s).label();
  }
  nrClasses_ = max+1; // for sake of simplicity I assume, that all natural numbers are classes between 0 and 'max'...

  means_.resize(nrClasses_);//init 2D vec.	   
  for(unsigned int lab=0; lab < means_.size(); ++lab)
	means_[lab].resize(features,0);   

  std::vector<int> num(nrClasses_,0);  // to calculate incremental average
  for(int s=0; s < size; ++s){  // for all samples...
    current_label=ds.label(s);  // remember its label
	num[current_label]++;
	for(int ft=0; ft < features; ++ft){ // add its features to the means
		means_[current_label][ft] +=  ds.sample(s).input(ft);
	}
  }
  pip::Image<unsigned char> out(pip::Dimension(28,28*10));
  double to_char(15500);
  double sum(0);
  int pos(0);
  for(unsigned int i=0; i < nrClasses_; ++i){
	sum=0;
	to_char=15500;
	for(int ft=0; ft < features; ++ft){ // add its features to the means
		 sum += means_[i][ft];
	}
	

	if(sum)
		to_char /= abs(sum);

	for(int ft=0; ft < features; ++ft){ // add its features to the means
		++pos;
		means_[i][ft] *= to_char;
		std::cout << means_[i][ft]<<std::endl;
	}
	sum=0;
  }
}